"""
调度器 - 负责优先级调度和时间片调度
"""
from datetime import datetime
from typing import List, Optional, Dict
import threading
from . import config
from .models import Request, ServiceStatus, WaitingStatus, RoomState
from .service_object import ServiceObject


class Scheduler:
    """调度器类"""
    
    def __init__(self, max_service_objects: int = config.MAX_SERVICE_OBJECTS):
        self.max_service_objects = max_service_objects
        
        # 创建服务对象池
        self.service_objects: List[ServiceObject] = [
            ServiceObject() for _ in range(max_service_objects)
        ]
        
        # 服务队列：正在服务的房间
        self.service_queue: Dict[str, ServiceStatus] = {}
        
        # 等待队列：等待服务的房间
        self.waiting_queue: List[WaitingStatus] = []
        
        self.lock = threading.RLock()
        
    def schedule_request(self, request: Request, room_states: Dict[str, RoomState]) -> bool:
        """调度请求
        
        Args:
            request: 温控请求
            room_states: 所有房间状态
            
        Returns:
            是否成功调度
        """
        with self.lock:
            room_id = request.room_id
            room_state = room_states[room_id]
            
            # 如果房间已经在服务队列中，检查是否是风速变化
            if room_id in self.service_queue:
                old_wind_speed = self.service_queue[room_id].wind_speed
                if old_wind_speed != request.wind_speed:
                    # 风速变化算作新请求，需要重新调度
                    self._release_service(room_id, room_states)
                else:
                    # 只是温度调节，不算新请求，直接更新目标温度
                    room_state.target_temp = request.target_temp
                    return True
            
            # 如果房间在等待队列中，更新请求
            self._remove_from_waiting_queue(room_id)
            
            # 获取空闲服务对象数量
            free_count = self._count_free_service_objects()
            
            if free_count > 0:
                # 有空闲服务对象，直接分配
                return self._allocate_service(request, room_state)
            else:
                # 没有空闲服务对象，启动调度策略
                return self._apply_scheduling_strategy(request, room_states)
    
    def _apply_scheduling_strategy(self, request: Request, 
                                   room_states: Dict[str, RoomState]) -> bool:
        """应用调度策略"""
        request_priority = request.get_priority()
        
        # 找出所有服务队列中风速低于请求的服务
        lower_priority_services = []
        equal_priority_services = []
        
        for room_id, status in self.service_queue.items():
            service_priority = config.WIND_PRIORITY.get(status.wind_speed, 0)
            if service_priority < request_priority:
                lower_priority_services.append((room_id, status))
            elif service_priority == request_priority:
                equal_priority_services.append((room_id, status))
        
        if lower_priority_services:
            # 优先级调度
            return self._priority_scheduling(request, lower_priority_services, room_states)
        elif equal_priority_services:
            # 时间片调度
            return self._time_slice_scheduling(request, room_states)
        else:
            # 请求优先级低于所有服务中的请求
            self._add_to_waiting_queue(request.room_id, request.wind_speed)
            room_states[request.room_id].is_waiting = True
            return False
    
    def _priority_scheduling(self, request: Request, 
                           lower_priority_services: List[tuple],
                           room_states: Dict[str, RoomState]) -> bool:
        """优先级调度"""
        if len(lower_priority_services) == 1:
            # 只有一个低优先级服务，释放它
            room_id, status = lower_priority_services[0]
            self._release_service(room_id, room_states)
            self._add_to_waiting_queue(room_id, status.wind_speed)
            room_states[room_id].is_waiting = True
        else:
            # 多个低优先级服务
            # 先按风速分组
            wind_groups = {}
            for room_id, status in lower_priority_services:
                wind_speed = status.wind_speed
                if wind_speed not in wind_groups:
                    wind_groups[wind_speed] = []
                wind_groups[wind_speed].append((room_id, status))
            
            if len(wind_groups) == 1:
                # 风速相等，选择服务时长最大的
                room_id, status = max(lower_priority_services, 
                                     key=lambda x: x[1].service_duration)
            else:
                # 风速不相等，选择风速最低的
                min_wind_priority = min(
                    config.WIND_PRIORITY.get(status.wind_speed, 0)
                    for _, status in lower_priority_services
                )
                candidates = [
                    (room_id, status) for room_id, status in lower_priority_services
                    if config.WIND_PRIORITY.get(status.wind_speed, 0) == min_wind_priority
                ]
                room_id, status = candidates[0]
            
            # 释放选中的服务
            self._release_service(room_id, room_states)
            self._add_to_waiting_queue(room_id, status.wind_speed)
            room_states[room_id].is_waiting = True
        
        # 分配服务给新请求
        return self._allocate_service(request, room_states[request.room_id])
    
    def _time_slice_scheduling(self, request: Request, 
                              room_states: Dict[str, RoomState]) -> bool:
        """时间片调度"""
        # 将请求加入等待队列
        self._add_to_waiting_queue(
            request.room_id, 
            request.wind_speed,
            config.WAITING_TIME_SLICE
        )
        room_states[request.room_id].is_waiting = True
        return False
    
    def check_waiting_queue(self, room_states: Dict[str, RoomState]) -> List[str]:
        """检查等待队列，处理等待时间到达的请求
        
        Returns:
            成功获得服务的房间ID列表
        """
        with self.lock:
            current_time = datetime.now()
            allocated_rooms = []
            
            # 按优先级排序等待队列
            self.waiting_queue.sort(
                key=lambda x: (
                    -config.WIND_PRIORITY.get(x.wind_speed, 0),  # 风速优先级（降序）
                    -int(x.priority_boosted),  # 已等待满s秒的优先（降序）
                    x.waiting_duration  # 等待时长（升序）
                )
            )
            
            # 处理等待队列
            to_remove = []
            for waiting in self.waiting_queue:
                room_id = waiting.room_id
                
                # 检查是否等待时间已到
                if waiting.is_ready(current_time) and not waiting.priority_boosted:
                    waiting.priority_boosted = True
                    
                    # 尝试抢占服务
                    if self._try_preempt_for_waiting(waiting, room_states):
                        to_remove.append(waiting)
                        allocated_rooms.append(room_id)
                    continue
                
                # 检查是否有空闲服务对象
                free_count = self._count_free_service_objects()
                if free_count > 0:
                    # 为等待时间最长的请求分配服务
                    room_state = room_states[room_id]
                    request = Request(
                        room_id=room_id,
                        target_temp=room_state.target_temp,
                        wind_speed=waiting.wind_speed,
                        mode=room_state.mode
                    )
                    if self._allocate_service(request, room_state):
                        to_remove.append(waiting)
                        allocated_rooms.append(room_id)
                        room_state.is_waiting = False
                        break
            
            # 移除已分配的等待请求
            for waiting in to_remove:
                if waiting in self.waiting_queue:
                    self.waiting_queue.remove(waiting)
            
            return allocated_rooms
    
    def _try_preempt_for_waiting(self, waiting: WaitingStatus, 
                                room_states: Dict[str, RoomState]) -> bool:
        """尝试为等待请求抢占服务"""
        waiting_priority = config.WIND_PRIORITY.get(waiting.wind_speed, 0)
        
        # 找出相同风速的服务
        same_priority_services = [
            (room_id, status) for room_id, status in self.service_queue.items()
            if config.WIND_PRIORITY.get(status.wind_speed, 0) == waiting_priority
        ]
        
        if same_priority_services:
            # 找出服务时长最长的
            room_id, status = max(same_priority_services, 
                                 key=lambda x: x[1].service_duration)
            
            # 释放该服务
            self._release_service(room_id, room_states)
            self._add_to_waiting_queue(room_id, status.wind_speed)
            room_states[room_id].is_waiting = True
            
            # 为等待请求分配服务
            room_state = room_states[waiting.room_id]
            request = Request(
                room_id=waiting.room_id,
                target_temp=room_state.target_temp,
                wind_speed=waiting.wind_speed,
                mode=room_state.mode
            )
            self._allocate_service(request, room_state)
            room_state.is_waiting = False
            return True
        
        return False
    
    def _allocate_service(self, request: Request, room_state: RoomState) -> bool:
        """分配服务对象"""
        # 找到空闲的服务对象
        for service_obj in self.service_objects:
            if service_obj.is_free:
                service_obj.allocate(request.room_id, room_state)
                
                # 更新房间状态
                room_state.target_temp = request.target_temp
                room_state.wind_speed = request.wind_speed
                room_state.mode = request.mode
                
                # 添加到服务队列
                status = ServiceStatus(
                    room_id=request.room_id,
                    service_object_id=service_obj.id,
                    wind_speed=request.wind_speed,
                    start_time=datetime.now()
                )
                self.service_queue[request.room_id] = status
                
                return True
        
        return False
    
    def _release_service(self, room_id: str, room_states: Dict[str, RoomState]):
        """释放服务"""
        if room_id not in self.service_queue:
            return
        
        status = self.service_queue[room_id]
        room_state = room_states[room_id]
        
        # 找到对应的服务对象
        for service_obj in self.service_objects:
            if service_obj.id == status.service_object_id:
                service_obj.release(room_state)
                break
        
        # 从服务队列移除
        del self.service_queue[room_id]
    
    def release_service_by_room(self, room_id: str, room_states: Dict[str, RoomState]):
        """根据房间ID释放服务（外部调用）"""
        with self.lock:
            self._release_service(room_id, room_states)
            self._remove_from_waiting_queue(room_id)
    
    def _add_to_waiting_queue(self, room_id: str, wind_speed: str, 
                             wait_time: float = config.WAITING_TIME_SLICE):
        """添加到等待队列"""
        # 检查是否已在等待队列
        for waiting in self.waiting_queue:
            if waiting.room_id == room_id:
                # 更新等待状态
                waiting.wind_speed = wind_speed
                waiting.start_wait_time = datetime.now()
                waiting.allocated_wait_time = wait_time
                waiting.waiting_duration = 0.0
                waiting.priority_boosted = False
                return
        
        # 添加新的等待状态
        waiting = WaitingStatus(
            room_id=room_id,
            wind_speed=wind_speed,
            waiting_duration=0.0,
            allocated_wait_time=wait_time,
            start_wait_time=datetime.now()
        )
        self.waiting_queue.append(waiting)
    
    def _remove_from_waiting_queue(self, room_id: str):
        """从等待队列移除"""
        self.waiting_queue = [w for w in self.waiting_queue if w.room_id != room_id]
    
    def _count_free_service_objects(self) -> int:
        """统计空闲服务对象数量"""
        return sum(1 for obj in self.service_objects if obj.is_free)
    
    def get_service_queue_info(self) -> List[dict]:
        """获取服务队列信息"""
        with self.lock:
            current_time = datetime.now()
            info = []
            for room_id, status in self.service_queue.items():
                status.update_duration(current_time)
                info.append({
                    'room_id': room_id,
                    'service_object_id': status.service_object_id,
                    'wind_speed': status.wind_speed,
                    'service_duration': round(status.service_duration, 2)
                })
            return info
    
    def get_waiting_queue_info(self) -> List[dict]:
        """获取等待队列信息"""
        with self.lock:
            current_time = datetime.now()
            info = []
            for waiting in self.waiting_queue:
                waiting.update_waiting_duration(current_time)
                info.append({
                    'room_id': waiting.room_id,
                    'wind_speed': waiting.wind_speed,
                    'waiting_duration': round(waiting.waiting_duration, 2),
                    'allocated_wait_time': waiting.allocated_wait_time,
                    'priority_boosted': waiting.priority_boosted
                })
            return info
    
    def update_service_durations(self):
        """更新所有服务时长"""
        with self.lock:
            current_time = datetime.now()
            for status in self.service_queue.values():
                status.update_duration(current_time)

